package gr.uop.cst.context.adaptation;

import gr.uop.cst.context.base.*;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;

/**
 *
 * @author Ioannis Bourlakos <mi49@uop.gr>
 */
public class ContextCondition<T extends Comparable> extends ContextObject<Boolean> implements Serializable {
    // TODO: define a proper set of method for ContextCondition handlers
    // TODO: define hard-coded values properly

    public static final int LESS_THAN = -1;
    public static final int EQUALS = 0;
    public static final int GREATER_THAN = 1;
    private ContextEvaluatable<T> o1;
    private int comparator;
    private ContextEvaluatable<T> o2;
    private final ArrayList<ContextConditionListener> conditionListeners;
    private Boolean previousValue = null;

    public ContextCondition(ContextEvaluatable<T> o1, int comparator, ContextEvaluatable<T> o2) {
        super(new ContextConditionProvider(), 1.0f, 1000, 2000, 2000, 4000);
        this.getProvider().addContextSource(new ContextConditionSource());
        this.o1 = o1;
        this.o2 = o2;
        this.comparator = comparator;
        this.conditionListeners = new ArrayList<ContextConditionListener>(1);
        this.initiate();
    }

    public ContextCondition(ContextEvaluatable<T> o1, int comparator, final T o2) {
        this(o1, comparator, new ContextEvaluatable<T>() {

            public T getCurrentValue() {
                return o2;
            }

            public ContextValueStatus getCurrentValueStatus() {
                return ContextValueStatus.VALID;
            }

            public void addContextValueListener(ContextValueListener<T> l) {
            }

            public void removeContextValueListener(ContextValueListener<T> l) {
            }
        });
    }

    public ContextCondition(final T o1, int comparator, ContextEvaluatable<T> o2) {
        this(new ContextEvaluatable<T>() {

            public T getCurrentValue() {
                return o1;
            }

            public ContextValueStatus getCurrentValueStatus() {
                return ContextValueStatus.VALID;
            }

            public void addContextValueListener(ContextValueListener<T> l) {
            }

            public void removeContextValueListener(ContextValueListener<T> l) {
            }
        }, comparator, o2);
    }

    public ContextCondition(final T o1, int comparator, final T o2) {
        this(new ContextEvaluatable<T>() {

            public T getCurrentValue() {
                return o1;
            }

            public ContextValueStatus getCurrentValueStatus() {
                return ContextValueStatus.VALID;
            }

            public void addContextValueListener(ContextValueListener<T> l) {
            }

            public void removeContextValueListener(ContextValueListener<T> l) {
            }
            
            @Override
            public String toString() {
                return getCurrentValue().toString();
            }
        }, comparator, new ContextEvaluatable<T>() {

            public T getCurrentValue() {
                return o2;
            }

            public ContextValueStatus getCurrentValueStatus() {
                return ContextValueStatus.VALID;
            }

            public void addContextValueListener(ContextValueListener<T> l) {
            }

            public void removeContextValueListener(ContextValueListener<T> l) {
            }
            
            @Override
            public String toString() {
                return getCurrentValue().toString();
            }
        });
    }
    
    

    @Override
    public synchronized final void addContextValueListener(ContextValueListener<Boolean> l) {
        if (l instanceof ContextConditionListener) {
            this.conditionListeners.add((ContextConditionListener) l);
        }
        super.addContextValueListener(l);
    }
    
    
    @Override
    public synchronized final void removeContextValueListener(ContextValueListener<Boolean> l) {
        ContextConditionListener ccl = null;
        if (l instanceof ContextConditionListener) {
            ccl = (ContextConditionListener) l;
            if (conditionListeners.contains(ccl)) {
                conditionListeners.remove(ccl);
            }
        }
        super.removeContextValueListener(l);
    }

    @Override
    protected synchronized void fireValueRefreshed(ContextValueEvent<Boolean> evt) {
        Boolean newValue = evt.getValue();
        ContextConditionEvent ccEvt = new ContextConditionEvent(this);
        if (newValue != previousValue) {
            if ((newValue == true)) {
                fireValueIsTrue(ccEvt);
            } else if (newValue == false) {
                fireValueIsFalse(ccEvt);
            } else {
                fireValueIsUnknown(ccEvt);
            }
            if ((newValue != null) && (previousValue != null)) {
                fireValueNegated(ccEvt);
            }
        }
        previousValue = newValue;
        super.fireValueRefreshed(evt);
    }

    protected synchronized void fireValueIsTrue(ContextConditionEvent evt) {
        for (ContextConditionListener l : this.conditionListeners) {
            l.valueIsTrue(evt);
        }
    }

    protected synchronized void fireValueIsFalse(ContextConditionEvent evt) {
        for (ContextConditionListener l : this.conditionListeners) {
            l.valueIsFalse(evt);
        }
    }

    protected synchronized void fireValueIsUnknown(ContextConditionEvent evt) {
        for (ContextConditionListener l : this.conditionListeners) {
            l.valueIsUnknown(evt);
        }
    }

    protected synchronized void fireValueNegated(ContextConditionEvent evt) {
        for (ContextConditionListener l : this.conditionListeners) {
            l.valueNegated(evt);
        }
    }
    
    public Boolean isTrue() {
        /*
        System.out.println("checking condition:"
//                + " source:" + getCurrentContextSource()
//                + " provider:" + getContextProvider()
                + " valid:" + this.isValid() 
                + " value:" + this.getCurrentValue()
                + " stamp:" + this.getCurrentTimestampRelative()
        );
         * 
         */
        return (this.isValid() && this.getCurrentValue());
        //return this.getCurrentValue();
    }
    
    @Override
    public String toString() {
        return o1.toString()+" -("+comparator+")- "+o2.toString();
    }

    private static class ContextConditionProvider extends ContextProvider<Boolean> {
    }

    private final class ContextConditionSource extends ContextSource<Boolean> {

        private boolean isAvailable;

        @Override
        public Boolean getValue() {
            T v1 = null;
            T v2 = null;
            int comp;
            isAvailable = true;
            try {
                v1 = o1.getCurrentValue();
                v2 = o2.getCurrentValue();
                //System.err.println("About to compare o1:" + o1 + " and o2:"+o2);
            } catch (ContextEvaluationException ex) {
                isAvailable = false;
                return false;   
            }
            comp = v1.compareTo(v2);
            return (Math.signum(comp) == comparator);
        }

        @Override
        public float getConfidence() {
            return 1.0f;
        }

        @Override
        public int getMinimumRefreshPeriod() {
            return 100;
        }

        @Override
        public boolean isAvailable() {
            return isAvailable;
        }
    }
    
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
    }

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
//        ContextConditionSource source = new ContextConditionSource();
//        this.getProvider().addContextSource(source);
        //System.err.println("Condition provider sources:"+getProvider().getContextSources().length);
        this.initiate();
    }
    
    
}
